package async;

import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author : zhouliangliang5
 * @date : 2023/3/17 11:03
 * @description :
 */
public class AsyncCompletableFuture {

//    CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
//        try {
//            Thread.sleep(100);
//            return "future1";
//        } catch (Exception e) {
//            return "future1";
//        }
//    });

//    CompletableFuture f2 = CompletableFuture.supplyAsync(() ->{
//        try {
//            Thread.sleep(200);
//            return "future2";
//        } catch (Exception e) {
//            return "future2";
//        }
//    });
//
//    CompletableFuture f3 = CompletableFuture.runAsync(()-> {
//        try {
//            Thread.sleep(300);
//            System.out.println("future3");
//        } catch (Exception e) {
//            System.out.println("future3");
//        }
//    });

    /**
     * 1. 执行异步任务，
     * 2. 失败时使用exceptionally中的值
     * 3. 执行完成后执行whenComplete中的方法
     */
    private void whenCompile() {

        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
                int i = 1/0;
                return "future1";
        }).exceptionally(new Function<Throwable, String>(){
            @Override
            public String apply(Throwable t) {
                int i = 1/0;
                return "执行失败 " + t.getMessage();
            }
        }).whenComplete(new BiConsumer<String, Throwable>() {
            @Override
            public void accept(String o, Throwable o2) {
                System.out.println( o + " in when complete");
            }
        }).whenComplete(new BiConsumer<String, Throwable>() {
            @Override
            public void accept(String s, Throwable throwable) {
                System.out.println( s + " in when complete 2");
            }
        });

    }

    /**
     * 获取执行结果，并传递给下一个流程执行
     */
    private void thenApply() {
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            int i = 1/0;
            return "10";
        }).thenApply(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.valueOf(s);
            }
        }).exceptionally(new Function<Throwable, Integer>() {
            @Override
            public Integer apply(Throwable throwable) {
                return 0;
            }
        }).whenComplete(new BiConsumer<Integer, Throwable>() {
            @Override
            public void accept(Integer integer, Throwable throwable) {
                System.out.println(integer);
            }
        });
    }

    /**
     * 获取执行结果，但是不会传递给下一个流程了
     */
    private void thenAccept() {
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            return "abc";
        }).thenAccept(value->{
            System.out.println(Integer.valueOf(value));
        }).exceptionally(new Function<Throwable, Void>() {
            @Override
            public Void apply(Throwable throwable) {
                System.out.println(throwable.getMessage());
                return null;
            }
        });
    }

    /**
     * 不关心结果，只串行执行
     */
    private void thenRun() {
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            System.out.println("abc");
            return "abc";
        }).thenRun(()->{
            System.out.println("123");
        }).thenRun(()->{
            System.out.println("def");
        });
        f1.join();
    }

    /**
     * handle用于处理异常
     */
    private void thenHandle() throws Exception{
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            return "10";
        }).thenApply(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                int i = 1/0;
                return Integer.valueOf(s);
            }
        }).handle(new BiFunction<Integer, Throwable, String>() {
            @Override
            public String apply(Integer integer, Throwable t) {
                if(t != null) {
                    System.out.println(t.getMessage());
                    return "error!";
                }
                return String.valueOf(integer);
            }
        });
        Object value = f1.get(100, TimeUnit.MILLISECONDS);
        if(value instanceof String) {
            System.out.println("value instanceof String");
        }
        System.out.println(value);
    }

    /**
     * 逐一合并异步程序的执行结果
     * @throws Exception
     */
    private void thenCombine() throws Exception{
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            return "123";
        }).thenCombine(CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            return "abc";
        }), new BiFunction<String, String, String>() {
            @Override
            public String apply(String s, String s2) {
                return s + s2;
            }
        }).thenCombineAsync(CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            return "456";
        }), new BiFunction<String, String, String>() {
            @Override
            public String apply(String s, String s2) {
                return s + s2;
            }
        });
        Object value = f1.get(100, TimeUnit.MILLISECONDS);
        System.out.println(value);
    }

    /**
     * 合并两个异步任务的结果，并消费，不会继续往下传递了
     * @throws Exception
     */
    private void thenAcceptBoth() throws Exception{
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            return "123";
        }).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
            return "abc";
        }), new BiConsumer<String, String>() {
            @Override
            public void accept(String s, String s2) {
                System.out.println(s + s2);
            }
        });
        Object value = f1.get(100, TimeUnit.MILLISECONDS);
        System.out.println(value);
    }

    /**
     * 两个线程执行完成后，执行其它的逻辑
     * @throws Exception
     */
    private void runAfterBoth() throws Exception {
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            return "123";
        }).runAfterBoth(CompletableFuture.supplyAsync(() -> {
            return "abc";
        }), new Runnable() {
            @Override
            public void run() {

            }
        });
    }

    /**
     * 等待任意一个线程执行完成，处理返回并传递结果
     * @throws Exception
     */
    private void applyToEither() throws Exception {
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            try {
                System.out.println("int 123");
                Thread.sleep(1000);
                return "123";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }).applyToEither(CompletableFuture.supplyAsync(() -> {
            System.out.println("int 456");
            return "456";
        }), new Function<String, String>() {
            @Override
            public String apply(String s) {
                System.out.println(s);
                return s;
            }
        });
        f1.join();
    }

    /**
     * 等待任意一个线程执行完成，消费结果
     * @throws Exception
     */
    private void acceptEither() throws Exception {
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            try {
                System.out.println("int 123");
                Thread.sleep(1000);
                return "123";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }).acceptEither(CompletableFuture.supplyAsync(() -> {
            System.out.println("int 456");
            int i = 1/0;
            return "456";
        }).exceptionally(new Function<Throwable, String>() {
            @Override
            public String apply(Throwable t) {
                System.out.println(t.getMessage());
                return "789";
            }
        }), new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        f1.join();
    }

    /**
     * 等待任意一个线程执行完成，然后执行另外的任务
     * @throws Exception
     */
    private void runAfterEither() throws Exception {
        CompletableFuture f1 = CompletableFuture.supplyAsync(()->{
            try {
                System.out.println("int 123");
                Thread.sleep(1000);
                return "123";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }).runAfterEither(CompletableFuture.supplyAsync(() -> {
            System.out.println("int 456");
            int i = 1/0;
            return "456";
        }).exceptionally(new Function<Throwable, String>() {
            @Override
            public String apply(Throwable t) {
                System.out.println(t.getMessage());
                return "789";
            }
        }), new Runnable() {
            @Override
            public void run() {
                return;
            }
        });
    }

    /**
     * 等待任意一个线程返回，可以获取返回值
     */
    private void anyOf() {
        Object value = CompletableFuture.anyOf(CompletableFuture.supplyAsync(()->{
            return "123";
        }), CompletableFuture.supplyAsync(()->{
            return "456";
        }), CompletableFuture.supplyAsync(()->{
            return "789";
        })).join();
        System.out.println(value);
    }

    /**
     * 等待所有的线程执行完成，无法获取返回值
     * @throws Exception
     */
    private void allOf() throws Exception{
        Object value = CompletableFuture.allOf(CompletableFuture.supplyAsync(()->{
            return "123";
        }), CompletableFuture.supplyAsync(()->{
            return "456";
        }), CompletableFuture.supplyAsync(()->{
            return "789";
        })).join();

        System.out.println(value);
    }

    public void testWhenCompile() {

        long start = System.currentTimeMillis();

        List<TestBean> list = new ArrayList<>();
        list.add(new TestBean());
        list.add(new TestBean());
        list.add(new TestBean());

        List<CompletableFuture<Integer>> futureList = list.stream().map(bean ->{
            return CompletableFuture.supplyAsync(()->{
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {

                }
                int i = 1/0;
                return 1;
            }).whenComplete(new BiConsumer<Integer, Throwable>() {
                @Override
                public void accept(Integer integer, Throwable throwable) {
                    bean.setIntValue(integer);
                }
            });
        }).collect(Collectors.toList());

        for (CompletableFuture<Integer> future : futureList) {
            future.join();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
        for(TestBean bean : list) {
            System.out.println(bean.getIntValue() + " " + bean.getStrValue());
        }
    }

    @Data
    public static class TestBean{
        private Integer intValue;
        private String strValue;
    }
    public static void main(String[] args) throws Exception{
        try {
            new AsyncCompletableFuture().testWhenCompile();
        } catch (Exception e) {
            System.out.println("e.initCause(e) : " + e.initCause(e));
//            System.out.println(e.getCause());
        }


//        AsyncCompletableFuture future = new AsyncCompletableFuture();
//        future.whenCompile();
//        future.thenApply();
//        future.thenAccept();
//        future.thenRun();
//        future.thenHandle();
//        future.thenCombine();
//        future.thenAcceptBoth();
//        future.runAfterBoth();
//        future.applyToEither();
//        future.acceptEither();
//        future.runAfterEither();
//        future.anyOf();
//        future.allOf();
    }
}
